package 多线程.threadlocal;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import xxx.Pet;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;

@Slf4j
public class ThreadLocalDemo {


    public static void main(String[] args) throws InterruptedException {
//        extractedX(Pet::new);
//        extractedX(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

//        wc();
    }


    private static void wc() throws InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        List<SimpleDateFormat> sdfs = getSimpleDateFormats();
        CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            threadPool.submit(() -> {
                Thread thread = Thread.currentThread();
                long id = thread.getId();
                System.out.println("id = " + id);
                int l = (int) (id % (sdfs.size() - 1));
                System.out.println("l = " + l);
                SimpleDateFormat sdf = sdfs.get(l);
                System.out.println(thread.getName() + "\t" + sdf);
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        threadPool.shutdown();
    }

    @NotNull
    private static List<SimpleDateFormat> getSimpleDateFormats() {
        List<SimpleDateFormat> sdfs = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            sdfs.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        }
        for (SimpleDateFormat sdf : sdfs) {
            System.out.println("sdf = " + sdf);
            System.out.println(sdf.hashCode());

        }
        System.out.println(sdfs.get(0) == sdfs.get(1));
        System.out.println(sdfs.get(0).equals(sdfs.get(1)));

        return sdfs;
    }

    private static void extracted4() throws InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        Map<String, ThreadLocal<Pet>> threadLocalMaps = new HashMap<>();
        // 缺陷1.多个线程操作一个map,应该用ConcurrentHashMap
        Map<String, List<Pet>> statistics = new ConcurrentHashMap<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);
        Object lock = new Object();
        for (int i = 0; i < 1000; i++) {
            threadPool.submit(() -> {
                String threadName = Thread.currentThread().getName();
                ThreadLocal<Pet> threadLocal = threadLocalMaps.get(threadName);
                /*
                 * 新建一个tl 保证每个线程里面都有一个自己的tl,
                 * Thread有个成员变量 ThreadLocalMap ,key为ThreadLocal,只要拿到key就能操作线程的 ThreadLocalMap
                 * 注意:双重检查
                 */
                if (threadLocal == null) {
                    synchronized (lock) {
                        threadLocal = threadLocalMaps.get(threadName);
                        if (threadLocal == null) {
                            threadLocal = ThreadLocal.withInitial(Pet::new);
                            threadLocalMaps.put(threadName, threadLocal);
                        }
                    }
                }
                Pet sdf = threadLocal.get();
                List<Pet> pets = statistics.computeIfAbsent(threadName, k -> new ArrayList<>());
                pets.add(sdf);
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        // 防止内存泄露,项目里面的线程池运行期间一般不会shutdown的
        for (ThreadLocal<Pet> threadLocal4remove : threadLocalMaps.values()) {
            threadLocal4remove.remove();
        }
        threadPool.shutdown();
        // 结果统计 一个线程里面只有一个Pet
        int count = 0;
        HashSet<Pet> sdfSet = new HashSet<>();
        for (Map.Entry<String, List<Pet>> entry : statistics.entrySet()) {
            String key = entry.getKey();
            List<Pet> pets = entry.getValue();
            sdfSet.addAll(pets);
            int size = pets.size();
            count += size;
            System.out.println(key + "\t" + size + "\t" + new HashSet<>(pets).size());
        }
        System.out.println(count);
        System.out.println(sdfSet.size());
    }

    private static void extractedX(Supplier<Object> supplier) throws InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        Map<String, ThreadLocal<Object>> threadLocalMaps = new HashMap<>();
        // 缺陷1.多个线程操作一个map,应该用ConcurrentHashMap
        Map<String, List<Object>> statistics = new ConcurrentHashMap<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);
        Object lock = new Object();
        for (int i = 0; i < 1000; i++) {
            threadPool.submit(() -> {
                String threadName = Thread.currentThread().getName();
                ThreadLocal<Object> threadLocal = threadLocalMaps.get(threadName);
                /*
                 * 新建一个tl 保证每个线程里面都有一个自己的tl,
                 * Thread有个成员变量 ThreadLocalMap ,key为ThreadLocal,只要拿到key就能操作线程的 ThreadLocalMap
                 * 注意:双重检查
                 */
                if (threadLocal == null) {
                    synchronized (lock) {
                        threadLocal = threadLocalMaps.get(threadName);
                        if (threadLocal == null) {
                            threadLocal = ThreadLocal.withInitial(supplier);
                            threadLocalMaps.put(threadName, threadLocal);
                        }
                    }
                }
                Object sdf = threadLocal.get();
                List<Object> pets = statistics.computeIfAbsent(threadName, k -> new ArrayList<>());
                pets.add(sdf);
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        // 防止内存泄露,项目里面的线程池运行期间一般不会shutdown的
        for (ThreadLocal<Object> threadLocal4remove : threadLocalMaps.values()) {
            threadLocal4remove.remove();
        }
        threadPool.shutdown();
        // 结果统计 一个线程里面只有一个Pet
        int count = 0;
        HashSet<Object> sdfSet = new HashSet<>();
        for (Map.Entry<String, List<Object>> entry : statistics.entrySet()) {
            String key = entry.getKey();
            List<Object> pets = entry.getValue();
            sdfSet.addAll(pets);
            int size = pets.size();
            count += size;
            System.out.println(key + "\t" + size + "\t" + new HashSet<>(pets).size());
        }
        System.out.println(count);
        System.out.println(sdfSet.size());
    }


}
